// define this macro to enable fast behavior simulation
// for flash by skipping SPI transfers
//`define FAST_FLASH

module spi_top_apb #(
    parameter flash_addr_start = 32'h30000000,
    parameter flash_addr_end   = 32'h3fffffff,
    parameter spi_ss_num       = 8
  ) (
    input         clock,
    input         reset,
    input  [31:0] in_paddr,
    input         in_psel,
    input         in_penable,
    input  [2:0]  in_pprot,
    input         in_pwrite,
    input  [31:0] in_pwdata,
    input  [3:0]  in_pstrb,
    output        in_pready,
    output [31:0] in_prdata,
    output        in_pslverr,
  
    output                  spi_sck,
    output [spi_ss_num-1:0] spi_ss,
    output                  spi_mosi,
    input                   spi_miso,
    output                  spi_irq_out
  );
  
  `ifdef FAST_FLASH
  // 不走spi
  wire [31:0] data;
  parameter invalid_cmd = 8'h0;
  flash_cmd flash_cmd_i(
    .clock(clock),
    .valid(in_psel && !in_penable),
    .cmd(in_pwrite ? invalid_cmd : 8'h03),
    .addr({8'b0, in_paddr[23:2], 2'b0}),
    .data(data)
  );
  assign spi_sck    = 1'b0;
  assign spi_ss     = 8'b0;
  assign spi_mosi   = 1'b1;
  assign spi_irq_out= 1'b0;
  assign in_pslverr = 1'b0;
  assign in_pready  = in_penable && in_psel && !in_pwrite;
  assign in_prdata  = data[31:0];
  
  `else
  // add XIP
  localparam  ADDR_RX0          = 5'h00,
              ADDR_TX1          = 5'h04,
              ADDR_SS           = 5'h18,
              ADDR_CTRL         = 5'h10,    
              ADDR_DIV          = 5'h14;
  
  localparam  DATA_DIV          = 32'h00000001,
              DATA_SS           = 32'h00000001,
              DATA_CTRL_CONFIG  = 32'h00002540;                 
  
  typedef enum [2:0] {IDLE=3'd0,TX1=3'd1,DIV=3'd2,SS=3'd3,
                            CTRL=3'd4,WAIT=3'd5,READ=3'd6}state_t;
                            
  reg  [2:0]  state;
  reg  [2:0]  nextstate; // wire
  reg  [4:0]  wb_adr;
  reg  [31:0] wb_dat_i;
  reg         wb_we;  // write or read
  reg         wb_stb; // valid
  reg  [3:0]  wb_sel; // write byte sel
  wire [31:0] wb_dat_o;
  wire        wb_ack; // ready
  wire        spi_mode; // 1:spi , 0:xip
  assign in_pready = wb_ack && (state == READ || state == IDLE);
  assign in_prdata = (state == READ)?{wb_dat_o[7:0],wb_dat_o[15:8],wb_dat_o[23:16],wb_dat_o[31:24]}
                    :wb_dat_o;
  assign spi_mode  = (state == IDLE); 
  //----------------------------------------------------------------- 
  // State Switch
  //-----------------------------------------------------------------
  always@(posedge clock) begin
      if(reset)
          state <= IDLE;
      else 
          state <= nextstate;     
  end
  // check flash write operation
  always @(posedge clock ) begin
      if ((in_paddr[31:28] == 4'h3) && in_pwrite && in_psel && !in_penable)begin
        $display("FLASH NOT SUPPORT WRITE!!! at addr=%x",in_paddr); 
        $fatal;            
      end
  end
  always @(*) begin 
      nextstate = state;    
      case (state)
      IDLE:   if((in_paddr[31:28] == 4'h3) && in_psel && !in_penable)
                  nextstate = TX1;
              else
                  nextstate = state;
      TX1:    if(wb_stb && wb_ack) 
                  nextstate = DIV; 
              else
                  nextstate = state;
      DIV:    if(wb_stb && wb_ack) 
                  nextstate = SS; 
              else
                  nextstate = state;
      SS:     if(wb_stb && wb_ack) 
                  nextstate = CTRL; 
              else
                  nextstate = state; 
      CTRL:   if(wb_stb && wb_ack) 
                  nextstate = WAIT; 
              else
                  nextstate = state;                  
      WAIT:   if(wb_stb && wb_ack && wb_dat_o[8] == 0) // go_bsy neg
                  nextstate = READ; 
              else
                  nextstate = state;                 
      READ:   if(wb_stb && wb_ack) 
                  nextstate = IDLE; 
              else
                  nextstate = state; 
      default :   nextstate = state;  
      endcase
  end
  //----------------------------------------------------------------- 
  // State Output
  //-----------------------------------------------------------------
  always @(posedge clock) begin  
      case (nextstate)
      TX1: begin
          wb_adr      <= ADDR_TX1;
          wb_dat_i    <= {8'h03,in_paddr[23:0]};
          wb_we       <= 1'b1; 
          wb_stb      <= 1'b1; 
          wb_sel      <= 4'hf;   
      end
      DIV:begin 
          wb_adr      <= ADDR_DIV;
          wb_dat_i    <= DATA_DIV;
          wb_we       <= 1'b1; 
          wb_stb      <= 1'b1;
          wb_sel      <= 4'hf;  
      end
      SS:begin 
          wb_adr      <= ADDR_SS;
          wb_dat_i    <= DATA_SS;
          wb_we       <= 1'b1;
          wb_stb      <= 1'b1;  
          wb_sel      <= 4'hf; 
      end
      CTRL:begin 
          wb_adr      <= ADDR_CTRL;
          wb_dat_i    <= DATA_CTRL_CONFIG;
          wb_we       <= 1'b1; 
          wb_stb      <= 1'b1;
          wb_sel      <= 4'hf;  
      end
      WAIT:begin 
          wb_adr      <= ADDR_CTRL;
          wb_we       <= 1'b0; 
          wb_stb      <= 1'b1; 
      end            
      READ:begin 
          wb_adr      <= ADDR_RX0;
          wb_we       <= 1'b0; 
          wb_stb      <= 1'b1; 
      end
      default:begin        
          wb_we       <= 1'b0; 
          wb_stb      <= 1'b0; 
      end
      endcase
  end
  

  //Wishbone b1 not support pipeline
  spi_top u0_spi_top (
    .wb_clk_i(clock),
    .wb_rst_i(reset),
    .wb_adr_i(spi_mode?in_paddr[4:0]:wb_adr),
    .wb_dat_i(spi_mode?in_pwdata:wb_dat_i),     
    .wb_dat_o(wb_dat_o),
    .wb_sel_i(spi_mode?in_pstrb:wb_sel),
    .wb_we_i (spi_mode?in_pwrite:wb_we),  
    .wb_stb_i(spi_mode?in_psel:wb_stb),   
    .wb_cyc_i(in_penable),    // 块操作保持有效
    .wb_ack_o(wb_ack),
    .wb_err_o(in_pslverr),
    .wb_int_o(spi_irq_out),
  
    .ss_pad_o(spi_ss),
    .sclk_pad_o(spi_sck),
    .mosi_pad_o(spi_mosi),
    .miso_pad_i(spi_miso)
  );
  
  `endif // FAST_FLASH
  
endmodule
  

